/**
 * @file ConfigManager.cpp
 *
 * @author Alejandro Dario Simi
 * @url http://www.daemonraco.com
 * @date 2011-12-03
 */

#include <ConfigManager.hpp>

#include <fstream>
#include <iostream>
#include <sstream>
#include <stdlib.h>
#include <string>
#include <sys/stat.h>
using namespace std;

#include <defines.h>
#include <Utils.hpp>

ConfigManager::ConfigManager(string config) {
	this->_config = config;
	this->_isLoaded = false;
	this->_browser.active = false;
	this->_hasCheckBoxes = false;
	this->_startSmall = false;
	for(int i = 0; i < XADOS_CONF_MBUTTON_COUNT; i++) {
		this->_macroButtons[i].active = false;
	}
	for(int i = 0; i < XADOS_CONF_CBOXES_COUNT; i++) {
		this->_checkBoxes[i].active = false;
	}

	this->load();
}

ConfigManager::~ConfigManager() {
}

bool ConfigManager::addCheckBox(const vector<string> &conf) {
	bool out = false;

	int confSize = conf.size();
	if(confSize >= 3 && confSize <= 4) {
		// Checking position availability.
		bool full = true;
		int pos = -1;
		for(int i = 0; i < XADOS_CONF_CBOXES_COUNT; i++) {
			if(!this->_checkBoxes[i].active) {
				pos = i;
				full = false;
				break;
			}
		}

		// Loading checkbox information
		if(!full) {
			this->_checkBoxes[pos].active = true;
			this->_checkBoxes[pos].name = conf[1];
			this->_checkBoxes[pos].flag = conf[2];
			if(confSize >= 4) {
				this->_checkBoxes[pos].description = conf[3];
			}
			out = true;
		} else {
			cerr << XADOS_PROGRAM": Check Boxes list is full." << endl;
		}
	}

	return out;
}

bool ConfigManager::addMacroButton(const vector<string> &conf) {
	bool out = false;

	if(conf.size() >= 4 && conf.size() <= 6) {
		int pos = atoi(conf[1].c_str());

		// Checking position limits.
		if(pos >= XADOS_CONF_MBUTTON_COUNT || pos < 0) {
			pos = 0;
		}

		// Checking position availability.
		bool full = true;
		for(int i = pos, j = 0; j < XADOS_CONF_MBUTTON_COUNT; i++, j++) {
			if(i == XADOS_CONF_MBUTTON_COUNT) {
				i = 0;
			}
			if(!this->_macroButtons[i].active) {
				pos = i;
				full = false;
				break;
			}
		}

		// Loading button information
		if(!full) {
			this->_macroButtons[pos].active = true;
			this->_macroButtons[pos].name = conf[2];
			this->_macroButtons[pos].command = conf[3];
			if(conf.size() >= 5) {
				struct stat iSt;
				this->_macroButtons[pos].icon = conf[4];
				if(stat(this->_macroButtons[pos].icon.c_str(), &iSt) != 0) {
					this->_macroButtons[pos].icon = this->_configPath + "/" + conf[4];

					if(stat(this->_macroButtons[pos].icon.c_str(), &iSt) != 0) {
						cerr << XADOS_PROGRAM": Icon image '" << conf[4] << "' doesn't exist. Neither '"
								<< this->_macroButtons[pos].icon << "'" << endl;
						this->_macroButtons[pos].icon = "";
					}
				}
			}
			if(conf.size() == 6) {
				this->_macroButtons[pos].description = conf[5];
			}
			out = true;
		} else {
			cerr << XADOS_PROGRAM": Macro Buttons list is full" << endl;
		}
	}

	return out;
}

const ConfigManager::BrowserInfo &ConfigManager::browser() const {
	return this->_browser;
}

const ConfigManager::CheckBoxesInfo &ConfigManager::checkBoxes() const {
	return this->_checkBoxes;
}

string ConfigManager::checkBoxesKeyword() const{
	return this->_checkBoxesKeyword;
}

string ConfigManager::config() const {
	return this->_config;
}

int ConfigManager::expandConfigLine(const string line, vector<string> &lines) const {
	lines.clear();

	string piece;
	stringstream ss(line);
	while(getline(ss, piece, ':')) {
		lines.push_back(piece);
	}

	return lines.size();
}

bool ConfigManager::hasCheckBoxes() const{
	return this->_hasCheckBoxes;
}

bool ConfigManager::isLoaded() {
	return this->_isLoaded;
}

const map<string,string> &ConfigManager::keywords() const {
	return this->_keywords;
}

bool ConfigManager::load() {
	if(!this->isLoaded()) {
		this->_configPath = Utils::HomeDirectory() + "/" + XADOS_HOME_DIRECTORY + "/" + this->_config + XADOS_CONF_EXTENSION;

		this->_script = this->_configPath + "/" + XADOS_CONF_CONFIG_FILE;

		struct stat scriptStat;
		if(stat(this->_script.c_str(), &scriptStat) == 0) {
			fstream fscript(this->_script.c_str(), ios::in);
			if(fscript.is_open()) {
				char buffer[XADOS_CONF_MAX_LINE_SIZE];
				string line;
				vector<string> configStmt;
				int lineNumber = 0;

				while(!fscript.eof()) {
					lineNumber++;
					fscript.getline(buffer, sizeof(buffer));
					line = noCommentsConfigLine(buffer);
					if(expandConfigLine(line, configStmt)) {
						if(!this->loadConfigLine(configStmt)) {
							cerr << XADOS_PROGRAM": Line " << lineNumber << " ignored. Value: " << line << endl;
						}
					}
				}

				fscript.close();
				this->_isLoaded = true;
			} else {
				cerr << XADOS_PROGRAM << ": Unable to open configuration file '" << this->_script << "'." << endl;
			}
		} else {
			cerr << XADOS_PROGRAM": File '" << this->_script << "' seems to not exist." << endl;
		}
	}

	return this->isLoaded();
}

bool ConfigManager::loadConfigLine(const vector<string> &conf) {
	bool out = false;

	if(conf.size()) {
		if(conf[0] == "Main Program") {
			out = this->setMainProgram(conf);
		} else if(conf[0] == "Macro Button") {
			out = this->addMacroButton(conf);
		} else if(conf[0] == "Use Browser") {
			out = this->setBrowser(conf);
		} else if(conf[0] == "Use Check Boxes") {
			out = this->setCheckBoxes(conf);
		} else if(conf[0] == "Add Check Box") {
			out = this->addCheckBox(conf);
		} else if(conf[0] == "Use Menu") {
			///@todo
		} else if(conf[0] == "Main Program") {
			///@todo
		} else if(conf[0] == "Xados Logo") {
			out = this->setLogo(conf);
		} else if(conf[0] == "Add Menu") {
			///@todo
		} else if(conf[0] == "Set Name") {
			///@todo
		} else if(conf[0] == "Add Option") {
			///@todo
		} else if(conf[0] == "Add Check Option") {
			///@todo
		} else if(conf[0] == "Separator") {
			///@todo
		} else if(conf[0] == "Add Macro Button Option") {
			///@todo
		} else if(conf[0] == "Add Browser Option") {
			///@todo
		} else if(conf[0] == "Start Small") {
			if(conf.size() == 1) {
				this->_startSmall = true;
				out = true;
			}
		}
	}

	return out;
}

string ConfigManager::logo() const {
	return this->_logo;
}

const ConfigManager::MacroButtonInfo &ConfigManager::macroButtons() const {
	return this->_macroButtons;
}

string ConfigManager::noCommentsConfigLine(const string line) const {
	string out = line;

	size_t pos = line.find("#");
	if(pos != string::npos) {
		out = line.substr(0, pos);
	}

	return out;
}

const ConfigManager::MainProgram &ConfigManager::program() const {
	return this->_program;
}

string ConfigManager::replaceKeywords(const string &command) const {
	string out = command;

	map<string, string>::const_iterator i;
	for(i = this->keywords().begin(); i != this->keywords().end(); i++) {
		size_t pos;
		while((pos = out.find(i->first)) != string::npos) {
			out.replace(pos, i->first.length(), i->second);
		}
	}

	return out;
}

bool ConfigManager::setBrowser(const vector<string> &conf) {
	bool out = false;

	if(conf.size() == 3) {
		this->_browser.directory = conf[1];
		this->_browser.keyword   = conf[2];
		this->_browser.active    = true;

		this->_keywords[this->_browser.keyword] = "";

		out = true;
	}

	return out;
}

bool ConfigManager::setCheckBoxes(const vector<string> &conf) {
	bool out = false;

	if(conf.size() == 2) {
		this->_checkBoxesKeyword = conf[1];
		this->_hasCheckBoxes = true;

		this->_keywords[this->_checkBoxesKeyword] = "";

		out = true;
	}

	return out;
}

bool ConfigManager::setKeyword(const string &keyword, const string &replacement) {
	bool out = this->_keywords.find(keyword) != this->_keywords.end();
	this->_keywords[keyword] = replacement;
	return out;
}

bool ConfigManager::setLogo(const vector<string> &conf) {
	bool out = false;

	if(conf.size() == 2) {
		struct stat iSt;
		this->_logo = conf[1];
		if(stat(this->_logo.c_str(), &iSt) != 0) {
			this->_logo = this->_configPath + "/" + conf[1];
			if(stat(this->_logo.c_str(), &iSt) != 0) {
				cerr << XADOS_PROGRAM": Icon image '" << conf[1] << "' doesn't exist. Neither '" << this->_logo << "'"
						<< endl;
				this->_logo = "";
			} else {
				out = true;
			}
		} else {
			out = true;
		}
	}

	return out;
}
	bool ConfigManager::setMainProgram(const vector<string> &conf) {
	bool out = false;

	if(conf.size() == 4) {
		this->_program.name = conf[1];
		this->_program.keyword = conf[2];
		this->_program.description = conf[3];

		this->_keywords[this->_program.keyword] = this->_program.name;

		out = true;
	}

	return out;
}

bool ConfigManager::startSmall() const {
	return this->_startSmall;
}
